jetcrab\vm\executor\instruction_handlers/arithmetic.rs
1//! # Arithmetic Handler
2//!
3//! Handles all arithmetic operations in the VM including basic math operations,
4//! increment/decrement operations, and power operations.
5//!
6//! ## Operations Supported
7//!
8//! - **Basic Arithmetic**: add, subtract, multiply, divide, modulo
9//! - **Power Operations**: power (exponentiation)
10//! - **Increment/Decrement**: increment, decrement
11//! - **Unary Operations**: negate
12//!
13//! ## Error Handling
14//!
15//! All operations return `Result<(), ExecutionError>` and handle:
16//! - Stack underflow (insufficient operands)
17//! - Division by zero
18//! - Invalid numeric operations
19//!
20//! ## Usage
21//!
22//! ```rust
23//! use jetcrab::vm::executor::instruction_handlers::ArithmeticHandler;
24//! use jetcrab::vm::executor::traits::StackOperations;
25//!
26//! let mut stack = MyStack::new();
27//! stack.push(Value::Number(5.0));
28//! stack.push(Value::Number(3.0));
29//! ArithmeticHandler::add(&mut stack)?;
30//! // Stack now contains: [8.0]
31//! ```
32
33use crate::vm::executor::error_handler::ExecutionError;
34use crate::vm::executor::traits::StackOperations;
35use crate::vm::value::Value;
36
37/// Handles arithmetic operations for the VM
38pub struct ArithmeticHandler;
39
40impl ArithmeticHandler {
41 /// Adds the top two values on the stack
42 ///
43 /// Pops two values from the stack, adds them, and pushes the result.
44 /// Supports numeric addition and string concatenation.
45 ///
46 /// # Arguments
47 /// * `stack` - The stack to operate on
48 ///
49 /// # Returns
50 /// * `Ok(())` on success
51 /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
52 ///
53 /// # Examples
54 ///
55 /// ```rust
56 /// let mut stack = MyStack::new();
57 /// stack.push(Value::Number(3.0));
58 /// stack.push(Value::Number(5.0));
59 /// ArithmeticHandler::add(&mut stack)?;
60 /// assert_eq!(stack.pop(), Some(Value::Number(8.0)));
61 /// ```
62 pub fn add<S>(stack: &mut S) -> Result<(), ExecutionError>
63 where
64 S: StackOperations,
65 {
66 let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
67 let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
68
69 let result = match (a, b) {
70 (Value::Number(a), Value::Number(b)) => Value::Number(a + b),
71 (Value::String(a), Value::String(b)) => Value::String(a + &b),
72 (Value::String(a), Value::Number(b)) => Value::String(a + &b.to_string()),
73 (Value::Number(a), Value::String(b)) => Value::String(a.to_string() + &b),
74 _ => return Err(ExecutionError::TypeError("Cannot add non-numeric values".to_string())),
75 };
76
77 stack.push(result);
78 Ok(())
79 }
80
81 /// Subtracts the second value from the first value on the stack
82 ///
83 /// Pops two values from the stack, subtracts the second from the first,
84 /// and pushes the result.
85 ///
86 /// # Arguments
87 /// * `stack` - The stack to operate on
88 ///
89 /// # Returns
90 /// * `Ok(())` on success
91 /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
92 pub fn subtract<S>(stack: &mut S) -> Result<(), ExecutionError>
93 where
94 S: StackOperations,
95 {
96 let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
97 let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
98
99 let result = match (a, b) {
100 (Value::Number(a), Value::Number(b)) => Value::Number(a - b),
101 _ => return Err(ExecutionError::TypeError("Cannot subtract non-numeric values".to_string())),
102 };
103
104 stack.push(result);
105 Ok(())
106 }
107
108 /// Multiplies the top two values on the stack
109 ///
110 /// Pops two values from the stack, multiplies them, and pushes the result.
111 ///
112 /// # Arguments
113 /// * `stack` - The stack to operate on
114 ///
115 /// # Returns
116 /// * `Ok(())` on success
117 /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
118 pub fn multiply<S>(stack: &mut S) -> Result<(), ExecutionError>
119 where
120 S: StackOperations,
121 {
122 let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
123 let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
124
125 let result = match (a, b) {
126 (Value::Number(a), Value::Number(b)) => Value::Number(a * b),
127 _ => return Err(ExecutionError::TypeError("Cannot multiply non-numeric values".to_string())),
128 };
129
130 stack.push(result);
131 Ok(())
132 }
133
134 /// Divides the first value by the second value on the stack
135 ///
136 /// Pops two values from the stack, divides the first by the second,
137 /// and pushes the result. Handles division by zero.
138 ///
139 /// # Arguments
140 /// * `stack` - The stack to operate on
141 ///
142 /// # Returns
143 /// * `Ok(())` on success
144 /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
145 /// * `Err(ExecutionError::RuntimeError)` on division by zero
146 pub fn divide<S>(stack: &mut S) -> Result<(), ExecutionError>
147 where
148 S: StackOperations,
149 {
150 let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
151 let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
152
153 let result = match (a, b) {
154 (Value::Number(a), Value::Number(b)) => {
155 if b == 0.0 {
156 return Err(ExecutionError::RuntimeError("Division by zero".to_string()));
157 }
158 Value::Number(a / b)
159 }
160 _ => return Err(ExecutionError::TypeError("Cannot divide non-numeric values".to_string())),
161 };
162
163 stack.push(result);
164 Ok(())
165 }
166
167 /// Computes the remainder of division
168 ///
169 /// Pops two values from the stack, computes a % b, and pushes the result.
170 ///
171 /// # Arguments
172 /// * `stack` - The stack to operate on
173 ///
174 /// # Returns
175 /// * `Ok(())` on success
176 /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
177 /// * `Err(ExecutionError::RuntimeError)` on division by zero
178 pub fn modulo<S>(stack: &mut S) -> Result<(), ExecutionError>
179 where
180 S: StackOperations,
181 {
182 let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
183 let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
184
185 let result = match (a, b) {
186 (Value::Number(a), Value::Number(b)) => {
187 if b == 0.0 {
188 return Err(ExecutionError::RuntimeError("Modulo by zero".to_string()));
189 }
190 Value::Number(a % b)
191 }
192 _ => return Err(ExecutionError::TypeError("Cannot compute modulo of non-numeric values".to_string())),
193 };
194
195 stack.push(result);
196 Ok(())
197 }
198
199 /// Computes the power of the first value raised to the second value
200 ///
201 /// Pops two values from the stack, computes a^b, and pushes the result.
202 ///
203 /// # Arguments
204 /// * `stack` - The stack to operate on
205 ///
206 /// # Returns
207 /// * `Ok(())` on success
208 /// * `Err(ExecutionError::StackUnderflow)` if insufficient operands
209 pub fn power<S>(stack: &mut S) -> Result<(), ExecutionError>
210 where
211 S: StackOperations,
212 {
213 let b = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
214 let a = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
215
216 let result = match (a, b) {
217 (Value::Number(a), Value::Number(b)) => Value::Number(a.powf(b)),
218 _ => return Err(ExecutionError::TypeError("Cannot compute power of non-numeric values".to_string())),
219 };
220
221 stack.push(result);
222 Ok(())
223 }
224
225 /// Negates the top value on the stack
226 ///
227 /// Pops one value from the stack, negates it, and pushes the result.
228 ///
229 /// # Arguments
230 /// * `stack` - The stack to operate on
231 ///
232 /// # Returns
233 /// * `Ok(())` on success
234 /// * `Err(ExecutionError::StackUnderflow)` if stack is empty
235 pub fn negate<S>(stack: &mut S) -> Result<(), ExecutionError>
236 where
237 S: StackOperations,
238 {
239 let value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
240
241 let result = match value {
242 Value::Number(n) => Value::Number(-n),
243 _ => return Err(ExecutionError::TypeError("Cannot negate non-numeric value".to_string())),
244 };
245
246 stack.push(result);
247 Ok(())
248 }
249
250 /// Increments the top value on the stack by 1
251 ///
252 /// Pops one value from the stack, adds 1, and pushes the result.
253 ///
254 /// # Arguments
255 /// * `stack` - The stack to operate on
256 ///
257 /// # Returns
258 /// * `Ok(())` on success
259 /// * `Err(ExecutionError::StackUnderflow)` if stack is empty
260 pub fn increment<S>(stack: &mut S) -> Result<(), ExecutionError>
261 where
262 S: StackOperations,
263 {
264 let value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
265
266 let result = match value {
267 Value::Number(n) => Value::Number(n + 1.0),
268 _ => return Err(ExecutionError::TypeError("Cannot increment non-numeric value".to_string())),
269 };
270
271 stack.push(result);
272 Ok(())
273 }
274
275 /// Decrements the top value on the stack by 1
276 ///
277 /// Pops one value from the stack, subtracts 1, and pushes the result.
278 ///
279 /// # Arguments
280 /// * `stack` - The stack to operate on
281 ///
282 /// # Returns
283 /// * `Ok(())` on success
284 /// * `Err(ExecutionError::StackUnderflow)` if stack is empty
285 pub fn decrement<S>(stack: &mut S) -> Result<(), ExecutionError>
286 where
287 S: StackOperations,
288 {
289 let value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
290
291 let result = match value {
292 Value::Number(n) => Value::Number(n - 1.0),
293 _ => return Err(ExecutionError::TypeError("Cannot decrement non-numeric value".to_string())),
294 };
295
296 stack.push(result);
297 Ok(())
298 }
299}